# Validation Library
# Can be used for any sort of validation in python
# It is flexible enough for you to provide your own validators also. 
# By - Nisarg Shah
# Version 1 - Jan 29, 2011

import re

# internal class that is used to keep things together
class ValidationClosure:
	def __init__(self, name, value, validators):
		self.name = name
		self.value = value
		self.validators = validators

# main class
class Validation:
	def __init__(self):
		self.fields = []
		self.exception = ValidationExceptions()
	
	# add a single field to validate
	def addField(self, name, value, validators):
		self.fields.append(
			ValidationClosure(name, value, validators)
		)
	
	# add multiple fields to validate
	def addFields(self, fields):
		for field in fields:
			self.addField(field[0], field[1], field[2])
	
	# runs the validation
	def run(self):
		for field in self.fields:
			for validator in field.validators:
				try:
					validator.validate(field)
				except ValidationException, e:
					self.exception.addException(e)
			
		if(len(self.exception.exceptions) > 0):
			raise self.exception

# If a field is wrong, ValidationException is used to group message and field together
class ValidationException(Exception):
	def __init__(self, field, msg):
		self.field = field
		msg = msg.replace('{name}', field.name)
		msg = msg.replace('{value}', field.value)
		self.msg = msg
	
	def __repr__(self):
		return self.msg

# this exception is thrown if some error occurs. Each exception in self.exception is 
# a ValidationException (type).
class ValidationExceptions(Exception):
	def __init__(self):
		self.exceptions = []
	
	def addException(self, ex):
		self.exceptions.append(ex)

### VALIDATORS ###

class REQUIRED:
	def __init__(self, message='{name} is required'):
		self.errorMessage = message
	
	def validate(self, field):
		if(field.value == ''):
			raise ValidationException(field, self.errorMessage)

class ALPHA:
	def __init__(self, message='{name} must be letters only'):
		self.errorMessage = message
	
	def validate(self, field):
		if not re.match('^[a-zA-Z]*$', field.value):
			raise ValidationException(field, self.errorMessage)

class ALPHANUMERIC:
	def __init__(self, message='{name} must be letters and numbers'):
		self.errorMessage = message

	def validate(self, field):
		if not re.match('^[a-zA-Z0-9]*$', field.value):
			raise ValidationException(field, self.errorMessage)

class EMAIL:
	def __init__(self, message='{name} must be a proper email address'):
		self.errorMessage = message

	def validate(self, field):
		if not re.match('''^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$''', field.value):
			raise ValidationException(field, self.errorMessage)

class NUMBER:
	def __init__(self, message='{name} must be a number', numRange=[None, None]):
		self.errorMessage = message
		self.range = numRange

	def validate(self, field):
		try:
			value = float(field.value)
		except ValueError:
			raise ValidationException(field, self.errorMessage)
		
		if(self.range[0] != None):
			if(value < self.range[0]):
				raise ValidationException(field, self.errorMessage)
		
		if(self.range[1] != None):
			if(value > self.range[1]):
				raise ValidationException(field, self.errorMessage)